Native macOS agent that keeps the office VPN and the NAS shares working without the shell wrapper it replaces. - VPN: full port of the `ovpn` script — host-route pinning, gateway discovery via networksetup, tunnel liveness by address plus pushed route, startup grace, restart of a wedged daemon. Root steps are batched into one system authorization prompt. - SMB: mounts via NetFS, discovers shares from the server, detects mounts event-driven through NSWorkspace, matches the host by resolved alias so externally created mounts are recognised. - Credentials never pass through the process: the VPN auth file is written by `security` straight to disk, and the password is stored via SecItemAdd rather than a command line visible in ps. - UI: status window, settings editor, ⌘+/⌘− zoom that re-lays out rather than scaling a bitmap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
51 lines
1.5 KiB
Swift
51 lines
1.5 KiB
Swift
import Foundation
|
|
|
|
enum LinkState: String {
|
|
case unknown
|
|
case ok
|
|
case nasDown
|
|
case vpnDown
|
|
|
|
var title: String {
|
|
switch self {
|
|
case .unknown: return "Проверяю…"
|
|
case .ok: return "NAS доступен"
|
|
case .nasDown: return "VPN жив, NAS не отвечает"
|
|
case .vpnDown: return "Офисный VPN не поднят"
|
|
}
|
|
}
|
|
|
|
var notificationBody: String {
|
|
switch self {
|
|
case .unknown: return ""
|
|
case .ok: return "Офисный NAS снова доступен"
|
|
case .nasDown: return "Туннель поднят, но NAS не отвечает на 445"
|
|
case .vpnDown: return "Офисный VPN отвалился"
|
|
}
|
|
}
|
|
|
|
/// Shown next to the menu bar icon. Empty when healthy — a permanent label
|
|
/// would be ignored within a day, which is the whole reason it is only
|
|
/// drawn when something is actually wrong.
|
|
var badge: String {
|
|
switch self {
|
|
case .unknown, .ok: return ""
|
|
case .nasDown: return "NAS"
|
|
case .vpnDown: return "VPN"
|
|
}
|
|
}
|
|
|
|
var symbolName: String {
|
|
switch self {
|
|
case .unknown: return "externaldrive.badge.questionmark"
|
|
case .ok: return "externaldrive.connected.to.line.below"
|
|
case .nasDown: return "externaldrive.badge.exclamationmark"
|
|
case .vpnDown: return "externaldrive.badge.xmark"
|
|
}
|
|
}
|
|
|
|
var isTunnelUp: Bool {
|
|
self == .ok || self == .nasDown
|
|
}
|
|
}
|