820c2448f8
Add peer and Issue new config offer the same choice as the web UI: show the config now or send a one-time setup link (1 h, 24 h or 7 d, PIN on by default). The link sheet shares the link through the iOS share sheet and shows the PIN, expiry and a QR code of the link. The peer page gets a setup link card with share, copy and revoke.
287 lines
6.8 KiB
Swift
287 lines
6.8 KiB
Swift
import Foundation
|
|
|
|
// Types mirror the JSON of GHOSTWIRE's /api/v1. Traffic is from the peer's
|
|
// point of view: down is what the peer downloaded, up what it uploaded.
|
|
|
|
nonisolated struct Me: Decodable {
|
|
let name: String
|
|
let isAdmin: Bool
|
|
let scope: String
|
|
let version: String
|
|
}
|
|
|
|
nonisolated struct HealthCheck: Decodable, Hashable {
|
|
let name: String
|
|
let ok: Bool
|
|
let detail: String
|
|
}
|
|
|
|
nonisolated struct PeerCounts: Decodable {
|
|
let total, enabled, online, disabled, never: Int
|
|
}
|
|
|
|
nonisolated struct Traffic: Decodable {
|
|
let down: Int64
|
|
let up: Int64
|
|
}
|
|
|
|
nonisolated struct Status: Decodable {
|
|
let version: String
|
|
let interface: String
|
|
let listenPort: Int
|
|
let endpoint: String
|
|
let ipv4: String
|
|
let ipv6: String
|
|
let ipv6Enabled: Bool
|
|
let capacity: Int
|
|
let started: Date
|
|
let healthy: Bool
|
|
let checks: [HealthCheck]
|
|
let peers: PeerCounts
|
|
let traffic24h: Traffic
|
|
let traffic30d: Traffic
|
|
let topPeer30d: String
|
|
}
|
|
|
|
nonisolated struct StatPoint: Decodable, Identifiable, Hashable {
|
|
let t: Int64
|
|
let down: Int64
|
|
let up: Int64
|
|
var id: Int64 { t }
|
|
var date: Date { Date(timeIntervalSince1970: TimeInterval(t)) }
|
|
}
|
|
|
|
nonisolated struct StatsResponse: Decodable {
|
|
let range: String
|
|
let points: [StatPoint]
|
|
}
|
|
|
|
nonisolated struct PeerStats: Decodable, Hashable {
|
|
let online: Bool
|
|
let lastHandshake: Date?
|
|
let endpoint: String
|
|
let down24h, up24h, down30d, up30d, downTotal, upTotal: Int64
|
|
let location: GeoInfo?
|
|
}
|
|
|
|
/// Country and network of an address, from the server's DB-IP lookup.
|
|
nonisolated struct GeoInfo: Decodable, Hashable {
|
|
let country: String?
|
|
let countryName: String?
|
|
let asn: Int?
|
|
let network: String?
|
|
|
|
/// "Germany · Deutsche Telekom AG" or "Local network".
|
|
var label: String {
|
|
[countryName ?? country, network].compactMap { $0 }.filter { !$0.isEmpty }.joined(separator: " · ")
|
|
}
|
|
}
|
|
|
|
/// One row of a peer's connection history.
|
|
nonisolated struct ConnSession: Decodable, Identifiable, Hashable {
|
|
let start: Date
|
|
let end: Date
|
|
let open: Bool
|
|
let seconds: Int64
|
|
let endpoint: String
|
|
let ip: String
|
|
let geo: GeoInfo?
|
|
let down: Int64
|
|
let up: Int64
|
|
var id: String { "\(start.timeIntervalSince1970)-\(ip)" }
|
|
}
|
|
|
|
nonisolated struct SessionsResponse: Decodable {
|
|
let sessions: [ConnSession]
|
|
}
|
|
|
|
nonisolated struct Peer: Decodable, Identifiable, Hashable {
|
|
let id: String
|
|
let name: String
|
|
let note: String
|
|
let enabled: Bool
|
|
let publicKey: String
|
|
let hasPresharedKey: Bool
|
|
let ipv4: String
|
|
let ipv6: String?
|
|
let dns: [String]? // nil = server default
|
|
let allowedIPs: [String]? // nil = server default
|
|
let keepalive: Int? // nil = server default
|
|
let effectiveDNS: [String]
|
|
let effectiveAllowedIPs: [String]
|
|
let effectiveKeepalive: Int
|
|
let created: Date
|
|
let configIssued: Date?
|
|
let setup: SetupInfo? // pending setup link, nil if none
|
|
let stats: PeerStats
|
|
}
|
|
|
|
/// A pending setup link as peer lists show it. The link itself is not in it.
|
|
nonisolated struct SetupInfo: Decodable, Hashable {
|
|
let expires: Date
|
|
let expired: Bool
|
|
let pinRequired: Bool
|
|
let pinFails: Int
|
|
}
|
|
|
|
nonisolated struct PeerList: Decodable {
|
|
let peers: [Peer]
|
|
let capacity: Int
|
|
let network: String
|
|
}
|
|
|
|
nonisolated struct PeerResult: Decodable {
|
|
let peer: Peer
|
|
let applyError: String
|
|
}
|
|
|
|
/// A freshly issued client config. The private key exists only here.
|
|
nonisolated struct IssuedConfig: Decodable, Identifiable {
|
|
let peer: Peer
|
|
let config: String
|
|
let qr: String?
|
|
let includesPrivateKey: Bool
|
|
let applyError: String
|
|
var id: String { peer.id + peer.publicKey }
|
|
}
|
|
|
|
/// A setup link for the admin to send. It sets up a device once.
|
|
nonisolated struct SetupSecret: Decodable, Hashable {
|
|
let url: String
|
|
let path: String
|
|
let pin: String?
|
|
let expires: Date
|
|
let qr: String
|
|
}
|
|
|
|
/// The answer to creating a peer or issuing a config with a setup link.
|
|
nonisolated struct LinkCreated: Decodable {
|
|
let peer: Peer
|
|
let setup: SetupSecret
|
|
let applyError: String
|
|
}
|
|
|
|
/// A config shown now, or a setup link to send.
|
|
enum IssueOutcome: Identifiable {
|
|
case config(IssuedConfig)
|
|
case link(LinkCreated)
|
|
|
|
var id: String {
|
|
switch self {
|
|
case .config(let c): c.id
|
|
case .link(let l): l.setup.path
|
|
}
|
|
}
|
|
|
|
var peer: Peer {
|
|
switch self {
|
|
case .config(let c): c.peer
|
|
case .link(let l): l.peer
|
|
}
|
|
}
|
|
|
|
var applyError: String {
|
|
switch self {
|
|
case .config(let c): c.applyError
|
|
case .link(let l): l.applyError
|
|
}
|
|
}
|
|
}
|
|
|
|
nonisolated struct PeerOnly: Decodable {
|
|
let peer: Peer
|
|
}
|
|
|
|
nonisolated struct ClientDefaults: Codable, Equatable {
|
|
var dns: [String]
|
|
var allowedIPs: [String]
|
|
var keepalive: Int
|
|
}
|
|
|
|
/// Server settings as GET/PATCH /server use them.
|
|
nonisolated struct ServerConfig: Codable, Equatable {
|
|
var interface: String
|
|
var publicKey: String
|
|
var keyCreated: Date
|
|
var listenPort: Int
|
|
var mtu: Int
|
|
var ipv4: String
|
|
var ipv6: String
|
|
var ipv6Enabled: Bool
|
|
var endpoint: String
|
|
var endpointPort: Int
|
|
var uplinkV4: String
|
|
var uplinkV6: String
|
|
var detectedUplinkV4: String
|
|
var detectedUplinkV6: String
|
|
var nat: Bool
|
|
var peerToPeer: Bool
|
|
var lanAccess: Bool
|
|
var openPort: Bool
|
|
var clientDefaults: ClientDefaults
|
|
|
|
var networks: [String] { ipv6Enabled ? [ipv4, ipv6] : [ipv4] }
|
|
}
|
|
|
|
nonisolated struct ServerResult: Decodable {
|
|
let server: ServerConfig
|
|
let applyError: String
|
|
let reissueNeeded: Bool?
|
|
}
|
|
|
|
nonisolated struct TLSSettings: Codable, Equatable {
|
|
var mode: String
|
|
var domain: String?
|
|
var email: String?
|
|
var staging: Bool?
|
|
var certFile: String?
|
|
var keyFile: String?
|
|
}
|
|
|
|
nonisolated struct WebSettings: Codable, Equatable {
|
|
var listen: String
|
|
var httpListen: String
|
|
var tls: TLSSettings
|
|
var sessionHours: Int
|
|
}
|
|
|
|
nonisolated struct LogSettings: Codable, Equatable {
|
|
var level: String
|
|
var maxSizeMB: Int
|
|
var maxFiles: Int
|
|
}
|
|
|
|
nonisolated struct StatsSettings: Codable, Equatable {
|
|
var hourlyHours: Int
|
|
var dailyDays: Int
|
|
var geoip: Bool?
|
|
}
|
|
|
|
nonisolated struct GeoStatus: Decodable {
|
|
let enabled: Bool
|
|
let updated: Date?
|
|
}
|
|
|
|
nonisolated struct AppSettings: Decodable {
|
|
var web: WebSettings
|
|
var log: LogSettings
|
|
var stats: StatsSettings
|
|
var adminUsername: String
|
|
var fingerprint: String
|
|
var logPath: String
|
|
var geo: GeoStatus?
|
|
}
|
|
|
|
nonisolated struct SettingsResult: Decodable {
|
|
let ok: Bool
|
|
let restartRequired: Bool
|
|
}
|
|
|
|
nonisolated struct ApplyResult: Decodable {
|
|
let applyError: String?
|
|
}
|
|
|
|
nonisolated struct DetectedIP: Decodable {
|
|
let ip: String
|
|
}
|