Fix CampusNAS recovery after VPN loss
This commit is contained in:
@@ -50,6 +50,8 @@ final class AppModel: ObservableObject {
|
||||
private(set) var config: Config
|
||||
private var timer: Timer?
|
||||
private var checking = false
|
||||
private var checkStartedAt: Date?
|
||||
private var checkGeneration = 0
|
||||
/// Tracked separately from `state` so "just became reachable" can be told
|
||||
/// apart from "still reachable" when deciding to auto-mount.
|
||||
private var previousState: LinkState = .unknown
|
||||
@@ -114,12 +116,24 @@ final class AppModel: ObservableObject {
|
||||
|
||||
// MARK: - Probing
|
||||
|
||||
func checkNow() {
|
||||
guard !checking else { return }
|
||||
func checkNow(force: Bool = false) {
|
||||
// A network framework call is supposed to be bounded, but defensive
|
||||
// recovery matters here: one stuck check must not freeze the displayed
|
||||
// state for days. Normal overlapping timer ticks are still ignored.
|
||||
if checking && !force {
|
||||
let age = checkStartedAt.map { Date().timeIntervalSince($0) } ?? 0
|
||||
guard age > max(config.probeTimeoutSeconds * 3, 15) else { return }
|
||||
Log.write("probe watchdog: replacing check stuck for \(Int(age)) s")
|
||||
}
|
||||
|
||||
checkGeneration += 1
|
||||
let generation = checkGeneration
|
||||
checking = true
|
||||
checkStartedAt = Date()
|
||||
|
||||
Task { @MainActor in
|
||||
let next = await probe()
|
||||
guard generation == self.checkGeneration else { return }
|
||||
self.mounted = Mounter.mountedShares(host: config.nasHost)
|
||||
|
||||
let wasUnknown = self.previousState == .unknown
|
||||
@@ -131,6 +145,7 @@ final class AppModel: ObservableObject {
|
||||
self.previousState = next
|
||||
self.state = next
|
||||
self.checking = false
|
||||
self.checkStartedAt = nil
|
||||
|
||||
if notable { self.onNotableChange?(next) }
|
||||
|
||||
@@ -145,6 +160,13 @@ final class AppModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sleep invalidates tunnels and may pause run-loop timers. Re-probe as
|
||||
/// soon as macOS wakes, superseding a check that started before sleep.
|
||||
func wake() {
|
||||
Log.write("system wake: refreshing VPN and NAS state")
|
||||
checkNow(force: true)
|
||||
}
|
||||
|
||||
/// Explicit refresh always re-enumerates, unlike the polling path.
|
||||
func refreshAll() {
|
||||
Task { @MainActor in
|
||||
|
||||
Reference in New Issue
Block a user