Merge branch 'refactor-infrastructures'
This commit is contained in:
commit
c1d8233339
|
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- More infrastructure metadata.
|
||||
|
||||
## 1.6.0 Beta 1757 (2019-04-25)
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -169,7 +169,7 @@ extension Infrastructure.Name {
|
|||
}
|
||||
}
|
||||
|
||||
extension Pool {
|
||||
extension PoolGroup {
|
||||
var logo: UIImage? {
|
||||
return ImageAsset(name: country.lowercased()).image
|
||||
}
|
||||
|
|
|
@ -33,19 +33,25 @@ protocol ProviderPoolViewControllerDelegate: class {
|
|||
class ProviderPoolViewController: UIViewController {
|
||||
@IBOutlet private weak var tableView: UITableView!
|
||||
|
||||
private var models: [PoolModel] = []
|
||||
private var categories: [PoolCategory] = []
|
||||
|
||||
private var sortedGroupsByCategory: [String: [PoolGroup]] = [:]
|
||||
|
||||
private var currentPool: Pool?
|
||||
|
||||
weak var delegate: ProviderPoolViewControllerDelegate?
|
||||
|
||||
func setModels(_ models: [PoolModel], currentPoolId: String?) {
|
||||
self.models = models
|
||||
func setInfrastructure(_ infrastructure: Infrastructure, currentPoolId: String?) {
|
||||
categories = infrastructure.categories.sorted { $0.name.lowercased() < $1.name.lowercased() }
|
||||
|
||||
for c in categories {
|
||||
sortedGroupsByCategory[c.name] = c.groups.values.sorted()
|
||||
}
|
||||
|
||||
// XXX: uglyyy
|
||||
for m in models {
|
||||
for pools in m.poolsByGroup.values {
|
||||
for p in pools {
|
||||
for cat in categories {
|
||||
for group in cat.groups.values {
|
||||
for p in group.pools {
|
||||
if p.id == currentPoolId {
|
||||
currentPool = p
|
||||
return
|
||||
|
@ -78,12 +84,16 @@ class ProviderPoolViewController: UIViewController {
|
|||
|
||||
extension ProviderPoolViewController: UITableViewDataSource, UITableViewDelegate {
|
||||
private var selectedIndexPath: IndexPath? {
|
||||
for (i, model) in models.enumerated() {
|
||||
for entries in model.poolsByGroup.enumerated() {
|
||||
guard let _ = entries.element.value.firstIndex(where: { $0.id == currentPool?.id }) else {
|
||||
for (i, cat) in categories.enumerated() {
|
||||
guard let sortedGroups = sortedGroupsByCategory[cat.name] else {
|
||||
continue
|
||||
}
|
||||
for entries in cat.groups.enumerated() {
|
||||
let group = entries.element.value
|
||||
guard let _ = group.pools.firstIndex(where: { $0.id == currentPool?.id }) else {
|
||||
continue
|
||||
}
|
||||
guard let row = model.sortedGroups.firstIndex(of: entries.element.key) else {
|
||||
guard let row = sortedGroups.firstIndex(where: { $0.country == group.country && $0.area == group.area }) else {
|
||||
continue
|
||||
}
|
||||
return IndexPath(row: row, section: i)
|
||||
|
@ -93,48 +103,44 @@ extension ProviderPoolViewController: UITableViewDataSource, UITableViewDelegate
|
|||
}
|
||||
|
||||
func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return models.count
|
||||
return categories.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
guard models.count > 1 else {
|
||||
guard categories.count > 1 else {
|
||||
return nil
|
||||
}
|
||||
let model = models[section]
|
||||
return model.isFree ? L10n.Provider.Pool.Sections.Free.header : L10n.Provider.Pool.Sections.Paid.header
|
||||
let model = categories[section]
|
||||
return model.name
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
let model = models[section]
|
||||
return model.sortedGroups.count
|
||||
let model = categories[section]
|
||||
return model.groups.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let model = models[indexPath.section]
|
||||
let group = model.sortedGroups[indexPath.row]
|
||||
let groupPools = model.poolsByGroup[group]!
|
||||
guard let pool = groupPools.first else {
|
||||
let group = poolGroup(at: indexPath)
|
||||
guard let pool = group.pools.first else {
|
||||
fatalError("Empty pools in group \(group)")
|
||||
}
|
||||
|
||||
let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
|
||||
cell.imageView?.image = pool.logo
|
||||
cell.imageView?.image = group.logo
|
||||
cell.leftText = pool.localizedCountry
|
||||
if groupPools.count > 1 {
|
||||
if group.pools.count > 1 {
|
||||
cell.rightText = pool.area?.uppercased()
|
||||
cell.accessoryType = .detailDisclosureButton // no checkmark!
|
||||
} else {
|
||||
cell.rightText = pool.areaId?.uppercased()
|
||||
cell.rightText = pool.secondaryId
|
||||
}
|
||||
cell.isTappable = true
|
||||
return cell
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let model = models[indexPath.section]
|
||||
let group = model.sortedGroups[indexPath.row]
|
||||
let groupPools = model.poolsByGroup[group]!
|
||||
guard let pool = groupPools.randomElement() else {
|
||||
let group = poolGroup(at: indexPath)
|
||||
guard let pool = group.pools.randomElement() else {
|
||||
fatalError("Empty pools in group \(group)")
|
||||
}
|
||||
currentPool = pool
|
||||
|
@ -142,32 +148,38 @@ extension ProviderPoolViewController: UITableViewDataSource, UITableViewDelegate
|
|||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
|
||||
let model = models[indexPath.section]
|
||||
let group = model.sortedGroups[indexPath.row]
|
||||
let groupPools = model.poolsByGroup[group]!
|
||||
guard let pool = groupPools.first else {
|
||||
fatalError("Empty pools in group \(group)")
|
||||
}
|
||||
guard groupPools.count > 1 else {
|
||||
let group = poolGroup(at: indexPath)
|
||||
guard group.pools.count > 1 else {
|
||||
return
|
||||
}
|
||||
let vc = OptionViewController<Pool>()
|
||||
vc.title = pool.localizedCountry
|
||||
vc.options = groupPools.sorted {
|
||||
guard let lnum = $0.num, let ln = Int(lnum) else {
|
||||
vc.title = group.localizedCountry
|
||||
vc.options = group.pools.sorted {
|
||||
guard let lnum = $0.num else {
|
||||
return true
|
||||
}
|
||||
guard let rnum = $1.num, let rn = Int(rnum) else {
|
||||
guard let rnum = $1.num else {
|
||||
return false
|
||||
}
|
||||
return ln < rn
|
||||
guard lnum != rnum else {
|
||||
return $0.secondaryId < $1.secondaryId
|
||||
}
|
||||
return lnum < rnum
|
||||
}
|
||||
vc.selectedOption = currentPool
|
||||
vc.descriptionBlock = { $0.areaId ?? "" } // XXX: fail gracefully
|
||||
vc.descriptionBlock = { $0.secondaryId }
|
||||
vc.selectionBlock = {
|
||||
self.currentPool = $0
|
||||
self.delegate?.providerPoolController(self, didSelectPool: $0)
|
||||
}
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
}
|
||||
|
||||
private func poolGroup(at indexPath: IndexPath) -> PoolGroup {
|
||||
let model = categories[indexPath.section]
|
||||
guard let sortedGroups = sortedGroupsByCategory[model.name] else {
|
||||
fatalError("Missing sorted groups for category '\(model.name)'")
|
||||
}
|
||||
return sortedGroups[indexPath.row]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ class ServiceViewController: UIViewController, TableModelHost {
|
|||
|
||||
case .providerPoolSegueIdentifier:
|
||||
let vc = destination as? ProviderPoolViewController
|
||||
vc?.setModels(InfrastructureCache.shared.poolModels(for: uncheckedProviderProfile), currentPoolId: uncheckedProviderProfile.poolId)
|
||||
vc?.setInfrastructure(uncheckedProviderProfile.infrastructure, currentPoolId: uncheckedProviderProfile.poolId)
|
||||
vc?.delegate = self
|
||||
|
||||
case .endpointSegueIdentifier:
|
||||
|
@ -299,9 +299,6 @@ class ServiceViewController: UIViewController, TableModelHost {
|
|||
}
|
||||
self.lastInfrastructureUpdate = response.1
|
||||
self.tableView.reloadData()
|
||||
|
||||
// invalidate current pool cache
|
||||
InfrastructureCache.shared.removePoolModels(for: name)
|
||||
}
|
||||
if !isUpdating {
|
||||
hud.hide()
|
||||
|
|
|
@ -89,7 +89,7 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC
|
|||
guard let provider = selectedProfile as? ProviderConnectionProfile else {
|
||||
return
|
||||
}
|
||||
vc.setModels(InfrastructureCache.shared.poolModels(for: provider), currentPoolId: nil)
|
||||
vc.setInfrastructure(provider.infrastructure, currentPoolId: nil)
|
||||
vc.delegate = self
|
||||
}
|
||||
|
||||
|
|
|
@ -78,8 +78,7 @@
|
|||
0E57F64620C83FC7008323CF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E57F64420C83FC7008323CF /* LaunchScreen.storyboard */; };
|
||||
0E58BD9322404EF1006FB157 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BD9122404EF1006FB157 /* Intents.intentdefinition */; };
|
||||
0E58BF65224152F9006FB157 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 0E58BD9122404EF1006FB157 /* Intents.intentdefinition */; settings = {ATTRIBUTES = (no_codegen, ); }; };
|
||||
0E66A270225FE25800F9C779 /* PoolModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E66A26F225FE25800F9C779 /* PoolModel.swift */; };
|
||||
0E66A272225FE5FB00F9C779 /* InfrastructureCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E66A271225FE5FB00F9C779 /* InfrastructureCache.swift */; };
|
||||
0E66A270225FE25800F9C779 /* PoolCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E66A26F225FE25800F9C779 /* PoolCategory.swift */; };
|
||||
0E6BE13F20CFBAB300A6DD36 /* DebugLogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6BE13E20CFBAB300A6DD36 /* DebugLogViewController.swift */; };
|
||||
0E773BF8224BF37600CDDC8E /* ShortcutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E773BF7224BF37600CDDC8E /* ShortcutsViewController.swift */; };
|
||||
0E89DFCE213EEDFA00741BA1 /* WizardProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E89DFCD213EEDFA00741BA1 /* WizardProviderViewController.swift */; };
|
||||
|
@ -223,8 +222,7 @@
|
|||
0E5E5DDE215119AF00E318A3 /* VPNStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNStatus.swift; sourceTree = "<group>"; };
|
||||
0E5E5DE1215119DD00E318A3 /* VPNConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNConfiguration.swift; sourceTree = "<group>"; };
|
||||
0E5E5DE421511C5F00E318A3 /* GracefulVPN.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GracefulVPN.swift; sourceTree = "<group>"; };
|
||||
0E66A26F225FE25800F9C779 /* PoolModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PoolModel.swift; sourceTree = "<group>"; };
|
||||
0E66A271225FE5FB00F9C779 /* InfrastructureCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfrastructureCache.swift; sourceTree = "<group>"; };
|
||||
0E66A26F225FE25800F9C779 /* PoolCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PoolCategory.swift; path = ../Model/Profiles/PoolCategory.swift; sourceTree = "<group>"; };
|
||||
0E6BE13920CFB76800A6DD36 /* ApplicationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationError.swift; sourceTree = "<group>"; };
|
||||
0E6BE13E20CFBAB300A6DD36 /* DebugLogViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DebugLogViewController.swift; sourceTree = "<group>"; };
|
||||
0E773BF7224BF37600CDDC8E /* ShortcutsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -484,9 +482,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
0EBE3AA3213DC1B000BFA2F5 /* HostConnectionProfile.swift */,
|
||||
0E66A271225FE5FB00F9C779 /* InfrastructureCache.swift */,
|
||||
0E79D13E21919EC900BB5FB2 /* PlaceholderConnectionProfile.swift */,
|
||||
0E66A26F225FE25800F9C779 /* PoolModel.swift */,
|
||||
0E79D14021919F5600BB5FB2 /* ProfileKey.swift */,
|
||||
0EBE3AA4213DC1B000BFA2F5 /* ProviderConnectionProfile.swift */,
|
||||
);
|
||||
|
@ -529,6 +525,7 @@
|
|||
0EBE3A83213C6ADE00BFA2F5 /* InfrastructureFactory.swift */,
|
||||
0E8D97E121388B52006FB4A0 /* InfrastructurePreset.swift */,
|
||||
0ED31C0F20CF09A30027975F /* Pool.swift */,
|
||||
0E66A26F225FE25800F9C779 /* PoolCategory.swift */,
|
||||
0E533B152258E03B00EF94FC /* PoolGroup.swift */,
|
||||
0E39BCEF214B9EF10035E9DE /* WebServices.swift */,
|
||||
);
|
||||
|
@ -1016,7 +1013,7 @@
|
|||
files = (
|
||||
0E3152BD223FA03D00F61841 /* GroupConstants.swift in Sources */,
|
||||
0ECEB10A224FECEA00E9E551 /* DataUnit.swift in Sources */,
|
||||
0E66A270225FE25800F9C779 /* PoolModel.swift in Sources */,
|
||||
0E66A270225FE25800F9C779 /* PoolCategory.swift in Sources */,
|
||||
0E3152C2223FA04800F61841 /* MockVPNProvider.swift in Sources */,
|
||||
0E533B162258E03B00EF94FC /* PoolGroup.swift in Sources */,
|
||||
0E3152D2223FA05400F61841 /* DebugLog.swift in Sources */,
|
||||
|
@ -1047,7 +1044,6 @@
|
|||
0E3152CD223FA05400F61841 /* ConnectionProfile.swift in Sources */,
|
||||
0E3152BC223FA03D00F61841 /* ApplicationError.swift in Sources */,
|
||||
0E3152C9223FA04D00F61841 /* InfrastructureFactory.swift in Sources */,
|
||||
0E66A272225FE5FB00F9C779 /* InfrastructureCache.swift in Sources */,
|
||||
0E58BD9322404EF1006FB157 /* Intents.intentdefinition in Sources */,
|
||||
0E3152D3223FA05400F61841 /* EndpointDataSource.swift in Sources */,
|
||||
0E3152D4223FA05400F61841 /* Preferences.swift in Sources */,
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -153,9 +153,6 @@
|
|||
"account.cells.open_guide.caption" = "See your credentials";
|
||||
"account.cells.signup.caption" = "Register with %@";
|
||||
|
||||
"provider.pool.sections.free.header" = "Free";
|
||||
"provider.pool.sections.paid.header" = "Paid";
|
||||
|
||||
"endpoint.sections.location_addresses.header" = "Addresses";
|
||||
"endpoint.sections.location_protocols.header" = "Protocols";
|
||||
"endpoint.cells.any_address.caption" = "Automatic";
|
||||
|
|
|
@ -47,7 +47,7 @@ public class AppConstants {
|
|||
}
|
||||
|
||||
public class Web {
|
||||
private static let version = "v1"
|
||||
private static let version = "v2"
|
||||
|
||||
private static let baseURL = Repos.api.appendingPathComponent(version)
|
||||
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// InfrastructureCache.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 4/11/19.
|
||||
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
|
||||
//
|
||||
// https://github.com/passepartoutvpn
|
||||
//
|
||||
// This file is part of Passepartout.
|
||||
//
|
||||
// Passepartout is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Passepartout is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// TODO: retain max N pool models at a time (LRU)
|
||||
|
||||
public class InfrastructureCache {
|
||||
public static let shared = InfrastructureCache()
|
||||
|
||||
private var poolModelsByName: [Infrastructure.Name: [PoolModel]]
|
||||
|
||||
private init() {
|
||||
poolModelsByName = [:]
|
||||
}
|
||||
|
||||
public func poolModels(for provider: ProviderConnectionProfile) -> [PoolModel] {
|
||||
if let models = poolModelsByName[provider.name] {
|
||||
return models
|
||||
}
|
||||
let freeModel = PoolModel(isFree: true)
|
||||
let paidModel = PoolModel(isFree: false)
|
||||
for p in provider.infrastructure.pools {
|
||||
if p.isFree ?? false {
|
||||
freeModel.addPool(p)
|
||||
} else {
|
||||
paidModel.addPool(p)
|
||||
}
|
||||
// if p.id == currentPoolId {
|
||||
// currentPool = p
|
||||
// }
|
||||
}
|
||||
freeModel.sort()
|
||||
paidModel.sort()
|
||||
|
||||
var models: [PoolModel] = []
|
||||
if !freeModel.isEmpty {
|
||||
models.append(freeModel)
|
||||
}
|
||||
if !paidModel.isEmpty {
|
||||
models.append(paidModel)
|
||||
}
|
||||
poolModelsByName[provider.name] = models
|
||||
return models
|
||||
}
|
||||
|
||||
public func removePoolModels(for name: Infrastructure.Name? = nil) {
|
||||
if let name = name {
|
||||
poolModelsByName.removeValue(forKey: name)
|
||||
return
|
||||
}
|
||||
poolModelsByName.removeAll()
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// PoolModel.swift
|
||||
// PoolCategory.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 4/11/19.
|
||||
|
@ -25,34 +25,8 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public class PoolModel {
|
||||
public let isFree: Bool
|
||||
public struct PoolCategory: Codable {
|
||||
public let name: String
|
||||
|
||||
public var poolsByGroup: [PoolGroup: [Pool]]
|
||||
|
||||
public private(set) var sortedGroups: [PoolGroup]
|
||||
|
||||
public init(isFree: Bool) {
|
||||
self.isFree = isFree
|
||||
poolsByGroup = [:]
|
||||
sortedGroups = []
|
||||
}
|
||||
|
||||
public var isEmpty: Bool {
|
||||
return sortedGroups.isEmpty
|
||||
}
|
||||
|
||||
public func addPool(_ p: Pool) {
|
||||
let group = p.group()
|
||||
if var existingPools = poolsByGroup[group] {
|
||||
existingPools.append(p)
|
||||
poolsByGroup[group] = existingPools
|
||||
} else {
|
||||
poolsByGroup[group] = [p]
|
||||
}
|
||||
}
|
||||
|
||||
public func sort() {
|
||||
sortedGroups = poolsByGroup.keys.sorted()
|
||||
}
|
||||
public let groups: [String: PoolGroup]
|
||||
}
|
|
@ -167,12 +167,19 @@ public extension ProviderConnectionProfile {
|
|||
|
||||
public extension ProviderConnectionProfile {
|
||||
var mainAddress: String? {
|
||||
assert(pool != nil, "Getting provider main address but no pool set")
|
||||
return pool?.hostname
|
||||
guard let pool = pool else {
|
||||
assertionFailure("Getting provider main address but no pool set")
|
||||
return nil
|
||||
}
|
||||
return pool.hostname
|
||||
}
|
||||
|
||||
var addresses: [String] {
|
||||
return pool?.addresses() ?? []
|
||||
var addrs = pool?.addresses() ?? []
|
||||
if let pool = pool, let externalHostname = try? preset?.externalConfiguration(forKey: .hostname, infrastructureName: infrastructure.name, pool: pool) as? String {
|
||||
addrs.insert(externalHostname, at: 0)
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
var protocols: [EndpointProtocol] {
|
||||
|
|
|
@ -53,7 +53,7 @@ public struct Infrastructure: Codable {
|
|||
|
||||
public let name: Name
|
||||
|
||||
public let pools: [Pool]
|
||||
public let categories: [PoolCategory]
|
||||
|
||||
public let presets: [InfrastructurePreset]
|
||||
|
||||
|
@ -69,11 +69,27 @@ public struct Infrastructure: Codable {
|
|||
}
|
||||
|
||||
public func pool(for identifier: String) -> Pool? {
|
||||
return pools.first { $0.id == identifier }
|
||||
for cat in categories {
|
||||
for group in cat.groups.values {
|
||||
guard let found = group.pools.first(where: { $0.id == identifier }) else {
|
||||
continue
|
||||
}
|
||||
return found
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func pool(withPrefix prefix: String) -> Pool? {
|
||||
return pools.first { $0.id.hasPrefix(prefix) }
|
||||
for cat in categories {
|
||||
for group in cat.groups.values {
|
||||
guard let found = group.pools.first(where: { $0.id.hasPrefix(prefix) }) else {
|
||||
continue
|
||||
}
|
||||
return found
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func preset(for identifier: String) -> InfrastructurePreset? {
|
||||
|
|
|
@ -38,6 +38,8 @@ public struct InfrastructurePreset: Codable {
|
|||
case key
|
||||
|
||||
case wrapKeyData = "wrap.key.data"
|
||||
|
||||
case hostname
|
||||
}
|
||||
|
||||
public enum PresetKeys: String, CodingKey {
|
||||
|
@ -95,30 +97,52 @@ public struct InfrastructurePreset: Codable {
|
|||
public func hasProtocol(_ proto: EndpointProtocol) -> Bool {
|
||||
return configuration.sessionConfiguration.endpointProtocols?.firstIndex(of: proto) != nil
|
||||
}
|
||||
|
||||
public func externalConfiguration(forKey key: ExternalKey, infrastructureName: Infrastructure.Name, pool: Pool) throws -> Any? {
|
||||
guard let pattern = external?[key] else {
|
||||
return nil
|
||||
}
|
||||
let baseURL = infrastructureName.externalURL
|
||||
switch key {
|
||||
case .ca:
|
||||
let filename = pattern.replacingOccurrences(of: "${id}", with: pool.id)
|
||||
let caURL = baseURL.appendingPathComponent(filename)
|
||||
return CryptoContainer(pem: try String(contentsOf: caURL))
|
||||
|
||||
public func injectExternalConfiguration(_ configuration: inout TunnelKitProvider.ConfigurationBuilder, with name: Infrastructure.Name, pool: Pool) throws {
|
||||
case .wrapKeyData:
|
||||
let filename = pattern.replacingOccurrences(of: "${id}", with: pool.id)
|
||||
let tlsKeyURL = baseURL.appendingPathComponent(filename)
|
||||
let file = try String(contentsOf: tlsKeyURL)
|
||||
return StaticKey(file: file, direction: .client)
|
||||
|
||||
case .hostname:
|
||||
return pattern.replacingOccurrences(of: "${id}", with: pool.id)
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func injectExternalConfiguration(_ configuration: inout TunnelKitProvider.ConfigurationBuilder, with infrastructureName: Infrastructure.Name, pool: Pool) throws {
|
||||
guard let external = external, !external.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
let baseURL = name.externalURL
|
||||
|
||||
var sessionBuilder = configuration.sessionConfiguration.builder()
|
||||
if let pattern = external[.ca] {
|
||||
let filename = pattern.replacingOccurrences(of: "${id}", with: pool.id)
|
||||
let caURL = baseURL.appendingPathComponent(filename)
|
||||
sessionBuilder.ca = CryptoContainer(pem: try String(contentsOf: caURL))
|
||||
if let _ = external[.ca] {
|
||||
sessionBuilder.ca = try externalConfiguration(forKey: .ca, infrastructureName: infrastructureName, pool: pool) as? CryptoContainer
|
||||
}
|
||||
if let pattern = external[.wrapKeyData] {
|
||||
let filename = pattern.replacingOccurrences(of: "${id}", with: pool.id)
|
||||
let tlsKeyURL = baseURL.appendingPathComponent(filename)
|
||||
if let _ = external[.wrapKeyData] {
|
||||
if let dummyWrap = sessionBuilder.tlsWrap {
|
||||
let file = try String(contentsOf: tlsKeyURL)
|
||||
if let staticKey = StaticKey(file: file, direction: .client) {
|
||||
if let staticKey = try externalConfiguration(forKey: .wrapKeyData, infrastructureName: infrastructureName, pool: pool) as? StaticKey {
|
||||
sessionBuilder.tlsWrap = SessionProxy.TLSWrap(strategy: dummyWrap.strategy, key: staticKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let _ = external[.hostname] {
|
||||
sessionBuilder.hostname = try externalConfiguration(forKey: .hostname, infrastructureName: infrastructureName, pool: pool) as? String
|
||||
}
|
||||
configuration.sessionConfiguration = sessionBuilder.build()
|
||||
}
|
||||
|
||||
|
|
|
@ -26,19 +26,19 @@
|
|||
import Foundation
|
||||
import TunnelKit
|
||||
|
||||
public struct Pool: Codable, Hashable, CustomStringConvertible {
|
||||
public struct Pool: Codable, Hashable {
|
||||
public enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
|
||||
case name
|
||||
|
||||
case country
|
||||
|
||||
case extraCountries = "extra_countries"
|
||||
|
||||
case area
|
||||
|
||||
|
||||
case num
|
||||
|
||||
case isFree = "free"
|
||||
case tags
|
||||
|
||||
// case location
|
||||
|
||||
|
@ -49,29 +49,15 @@ public struct Pool: Codable, Hashable, CustomStringConvertible {
|
|||
|
||||
public let id: String
|
||||
|
||||
private let name: String
|
||||
|
||||
public let country: String
|
||||
|
||||
public let extraCountries: [String]?
|
||||
|
||||
public let area: String?
|
||||
|
||||
public let num: Int?
|
||||
|
||||
public let num: String?
|
||||
|
||||
public var areaId: String? {
|
||||
let id: String
|
||||
if let area = area, let num = num {
|
||||
id = "\(area) #\(num)"
|
||||
} else if let area = area {
|
||||
id = area
|
||||
} else if let num = num {
|
||||
id = "#\(num)"
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return id.uppercased()
|
||||
}
|
||||
|
||||
public let isFree: Bool?
|
||||
public let tags: [String]?
|
||||
|
||||
// public let location: (Double, Double)
|
||||
|
||||
|
@ -98,42 +84,49 @@ public struct Pool: Codable, Hashable, CustomStringConvertible {
|
|||
return addrs
|
||||
}
|
||||
|
||||
public func group() -> PoolGroup {
|
||||
return PoolGroup(country: country, area: area)
|
||||
}
|
||||
|
||||
// MARK: Hashable
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
id.hash(into: &hasher)
|
||||
}
|
||||
|
||||
// MARK: CustomStringConvertible
|
||||
|
||||
public var description: String {
|
||||
return "{[\(id)] \"\(name)\"}"
|
||||
}
|
||||
}
|
||||
|
||||
extension Pool {
|
||||
private static let localizedFormat = "%@ - %@"
|
||||
|
||||
public var localizedCountry: String {
|
||||
return Utils.localizedCountry(country)
|
||||
}
|
||||
|
||||
public var localizedId: String {
|
||||
let countryString = localizedCountry
|
||||
let zone: String
|
||||
if let area = area, let num = num {
|
||||
zone = "\(area) #\(num)"
|
||||
} else if let area = area {
|
||||
zone = area
|
||||
} else if let num = num {
|
||||
zone = "#\(num)"
|
||||
} else {
|
||||
return countryString
|
||||
var comps: [String] = [localizedCountry]
|
||||
if let secondaryId = optionalSecondaryId {
|
||||
comps.append(secondaryId)
|
||||
}
|
||||
return String.init(format: Pool.localizedFormat, countryString, zone.uppercased())
|
||||
return comps.joined(separator: " - ")
|
||||
}
|
||||
|
||||
public var secondaryId: String {
|
||||
return optionalSecondaryId ?? ""
|
||||
}
|
||||
|
||||
private var optionalSecondaryId: String? {
|
||||
var comps: [String] = []
|
||||
if let extraCountries = extraCountries {
|
||||
comps.append(contentsOf: extraCountries.map { Utils.localizedCountry($0) })
|
||||
}
|
||||
if let area = area {
|
||||
comps.append(area.uppercased())
|
||||
}
|
||||
if let num = num {
|
||||
comps.append("#\(num)")
|
||||
}
|
||||
guard !comps.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
var str = comps.joined(separator: " ")
|
||||
if let tags = tags {
|
||||
let suffix = tags.map { $0.uppercased() }.joined(separator: ",")
|
||||
str = "\(str) (\(suffix))"
|
||||
}
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,31 +25,27 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public struct PoolGroup: Hashable, Comparable, CustomStringConvertible {
|
||||
public struct PoolGroup: Codable, Hashable, Comparable, CustomStringConvertible {
|
||||
public let country: String
|
||||
|
||||
public let area: String?
|
||||
|
||||
private let id: String
|
||||
public let pools: [Pool]
|
||||
|
||||
private let localizedId: String
|
||||
|
||||
public init(country: String, area: String?) {
|
||||
self.country = country
|
||||
self.area = area
|
||||
|
||||
private var id: String {
|
||||
var id = country
|
||||
var localizedId = Utils.localizedCountry(country)
|
||||
if let area = area {
|
||||
id += area
|
||||
localizedId += area
|
||||
}
|
||||
self.id = id
|
||||
self.localizedId = localizedId
|
||||
return id
|
||||
}
|
||||
|
||||
public func contains(_ pool: Pool) -> Bool {
|
||||
return (pool.country == country) && (pool.area == area)
|
||||
private var localizedId: String {
|
||||
var localizedId = Utils.localizedCountry(country)
|
||||
if let area = area {
|
||||
localizedId += area
|
||||
}
|
||||
return localizedId
|
||||
}
|
||||
|
||||
// MARK: Hashable
|
||||
|
@ -70,3 +66,9 @@ public struct PoolGroup: Hashable, Comparable, CustomStringConvertible {
|
|||
return "{\(country), \(area ?? "--")}"
|
||||
}
|
||||
}
|
||||
|
||||
extension PoolGroup {
|
||||
public var localizedCountry: String {
|
||||
return Utils.localizedCountry(country)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/sh
|
||||
. .env.secret-deploy
|
||||
SRC=$PROJECT_ROOT/api/v1
|
||||
SRC=$PROJECT_ROOT/api/v2
|
||||
DST=$PROJECT_ROOT/passepartout-ios/Passepartout/Resources/Web
|
||||
rm -rf $DST && cp -pr $SRC $DST
|
||||
|
|
Loading…
Reference in New Issue