60 lines
1.6 KiB
Swift
60 lines
1.6 KiB
Swift
import Foundation
|
|
|
|
struct PairedNode: Codable, Equatable {
|
|
var nodeId: String
|
|
var displayName: String?
|
|
var platform: String?
|
|
var version: String?
|
|
var deviceFamily: String?
|
|
var modelIdentifier: String?
|
|
var token: String
|
|
var createdAtMs: Int
|
|
var lastSeenAtMs: Int?
|
|
}
|
|
|
|
actor PairedNodesStore {
|
|
private let fileURL: URL
|
|
private var nodes: [String: PairedNode] = [:]
|
|
|
|
init(fileURL: URL) {
|
|
self.fileURL = fileURL
|
|
}
|
|
|
|
func load() {
|
|
do {
|
|
let data = try Data(contentsOf: self.fileURL)
|
|
let decoded = try JSONDecoder().decode([String: PairedNode].self, from: data)
|
|
self.nodes = decoded
|
|
} catch {
|
|
self.nodes = [:]
|
|
}
|
|
}
|
|
|
|
func all() -> [PairedNode] {
|
|
self.nodes.values.sorted { a, b in (a.displayName ?? a.nodeId) < (b.displayName ?? b.nodeId) }
|
|
}
|
|
|
|
func find(nodeId: String) -> PairedNode? {
|
|
self.nodes[nodeId]
|
|
}
|
|
|
|
func upsert(_ node: PairedNode) async throws {
|
|
self.nodes[node.nodeId] = node
|
|
try await self.persist()
|
|
}
|
|
|
|
func touchSeen(nodeId: String) async throws {
|
|
guard var node = self.nodes[nodeId] else { return }
|
|
node.lastSeenAtMs = Int(Date().timeIntervalSince1970 * 1000)
|
|
self.nodes[nodeId] = node
|
|
try await self.persist()
|
|
}
|
|
|
|
private func persist() async throws {
|
|
let dir = self.fileURL.deletingLastPathComponent()
|
|
try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
|
|
let data = try JSONEncoder().encode(self.nodes)
|
|
try data.write(to: self.fileURL, options: [.atomic])
|
|
}
|
|
}
|