Addresses a multi-agent code review (26 confirmed findings + a URLSession leak) across correctness, concurrency, security, and cleanup: - MongoDocument.id: deterministic fallback (was UUID() on every access) to stop SwiftUI List/ForEach identity churn; getAll pagination drives its cursor off the real _id via documentId, so a missing _id stops paging instead of looping on a fabricated cursor. - View models: per-load generation guard (+ captured-tab guard on ONU detail) so a cancelled or overlapping load can't commit stale results or land data on the wrong tab. - JSONValue.intValue: guard Int(d) against NaN/inf/out-of-range (crash). - ServerTrustEvaluator: hash the real SubjectPublicKeyInfo (prepend the algorithm-specific ASN.1 SPKI header) so a standard openssl-captured public-key pin actually matches; unknown key types fail closed. - AppEnvironment.configure: clear stale cookies + Keychain creds on host change. - FirmwareFile.isCompatible: empty metadata no longer passes as compatible-for-all. - APIClient: invalidate URLSession on deinit (per-configure/probe leak). - Dashboard best-effort sections keep last-good on transient failure; unified optical thresholds; LoginView prefill-once; populatedBuckets unique ForEach id; Format rounding rollover; dead-code/docstring/dedup cleanups. Verification pending on the Mac (no Swift toolchain on the Linux dev box). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
108 lines
5.2 KiB
Swift
108 lines
5.2 KiB
Swift
import Foundation
|
|
|
|
/// Builds versioned endpoint paths (without host). The leading version segment
|
|
/// is applied here so call sites read like the REST docs.
|
|
///
|
|
/// Reconcile exact paths with `openapi.json`; these mirror the MCMS 6.0
|
|
/// REST API Developer Guide.
|
|
// Some cases below are not yet wired to any repository/UI — they are documented
|
|
// path scaffolding for upcoming features, NOT validated against a live box:
|
|
// `databaseStatus`, `controllerStates`, `onuAlarmConfigs`/`onuAlarmConfig`,
|
|
// `onuAutomationConfigs`/`onuAutomationConfig`. Confirm them before building on them.
|
|
enum MCMSEndpoint {
|
|
// Auth / session / system
|
|
case authenticate
|
|
case logout
|
|
case databaseSelection
|
|
case databaseStatus
|
|
case version
|
|
|
|
// PON Controllers
|
|
case controllerConfigs
|
|
case controllerConfig(id: String)
|
|
case controllerStates // CNTL-STATE list
|
|
case controllerStats(id: String)
|
|
case controllerLogs(id: String)
|
|
|
|
// OLTs
|
|
case oltConfigs
|
|
case oltConfig(id: String)
|
|
case oltStates // OLT-STATE list (carries ONU registration buckets)
|
|
case oltState(id: String)
|
|
case oltStats(id: String)
|
|
case oltLogs(id: String)
|
|
case oltDisableONU(olt: String, onu: String)
|
|
case oltBroadcastEnableONUs(olt: String)
|
|
case oltReset(id: String)
|
|
|
|
// ONUs
|
|
case onuConfigs
|
|
case onuConfig(id: String)
|
|
case onuStates
|
|
case onuState(id: String)
|
|
case onuStats(id: String)
|
|
case onuLogs(id: String)
|
|
case onuAlarmHistories
|
|
case onuAlarmHistory(id: String)
|
|
case onuAlarmConfigs
|
|
case onuAlarmConfig(id: String)
|
|
case onuCPEStates
|
|
case onuAutomationConfigs
|
|
case onuAutomationConfig(id: String)
|
|
case onuReset(id: String)
|
|
case onuFirmwareFiles // GET /files/onu-firmware/
|
|
case onuCPE(id: String) // web helper /cpe/onu/<id>/ (no version segment)
|
|
|
|
/// Path relative to `baseURL`, including the version segment.
|
|
func path(version: String) -> String {
|
|
let v = "/\(version)"
|
|
switch self {
|
|
case .authenticate: return "\(v)/users/authenticate/"
|
|
case .logout: return "\(v)/users/logout/"
|
|
case .databaseSelection: return "\(v)/databases/selection/"
|
|
case .databaseStatus: return "\(v)/databases/status/"
|
|
case .version: return "\(v)/ponmgr/version/"
|
|
|
|
case .controllerConfigs: return "\(v)/controllers/configs/"
|
|
case .controllerConfig(let id): return "\(v)/controllers/configs/\(enc(id))/"
|
|
case .controllerStates: return "\(v)/controllers/states/"
|
|
case .controllerStats(let id): return "\(v)/controllers/stats/\(enc(id))/"
|
|
case .controllerLogs(let id): return "\(v)/controllers/logs/\(enc(id))/"
|
|
|
|
case .oltConfigs: return "\(v)/olts/configs/"
|
|
case .oltConfig(let id): return "\(v)/olts/configs/\(enc(id))/"
|
|
case .oltStates: return "\(v)/olts/states/"
|
|
case .oltState(let id): return "\(v)/olts/states/\(enc(id))/"
|
|
case .oltStats(let id): return "\(v)/olts/stats/\(enc(id))/"
|
|
case .oltLogs(let id): return "\(v)/olts/logs/\(enc(id))/"
|
|
case .oltDisableONU(let olt, let onu):
|
|
return "\(v)/olts/\(enc(olt))/disable-onu/\(enc(onu))/"
|
|
case .oltBroadcastEnableONUs(let olt):
|
|
return "\(v)/olts/\(enc(olt))/broadcast-enable-onus/"
|
|
case .oltReset(let id): return "\(v)/olts/\(enc(id))/reset/"
|
|
|
|
case .onuConfigs: return "\(v)/onus/configs/"
|
|
case .onuConfig(let id): return "\(v)/onus/configs/\(enc(id))/"
|
|
case .onuStates: return "\(v)/onus/states/"
|
|
case .onuState(let id): return "\(v)/onus/states/\(enc(id))/"
|
|
case .onuStats(let id): return "\(v)/onus/stats/\(enc(id))/"
|
|
case .onuLogs(let id): return "\(v)/onus/logs/\(enc(id))/"
|
|
case .onuAlarmHistories: return "\(v)/onus/alarm-histories/"
|
|
case .onuAlarmHistory(let id): return "\(v)/onus/alarm-histories/\(enc(id))/"
|
|
case .onuAlarmConfigs: return "\(v)/onus/alarm-configs/"
|
|
case .onuAlarmConfig(let id): return "\(v)/onus/alarm-configs/\(enc(id))/"
|
|
case .onuCPEStates: return "\(v)/onus/cpe-states/"
|
|
case .onuAutomationConfigs: return "\(v)/onus/automation/configs/"
|
|
case .onuAutomationConfig(let id): return "\(v)/onus/automation/configs/\(enc(id))/"
|
|
case .onuReset(let id): return "\(v)/onus/\(enc(id))/reset/"
|
|
case .onuFirmwareFiles: return "\(v)/files/onu-firmware/"
|
|
case .onuCPE(let id): return "/cpe/onu/\(enc(id))/" // versionless web helper
|
|
}
|
|
}
|
|
|
|
/// IDs (serials / MAC addresses) are kept RAW here — the full path is
|
|
/// percent-encoded exactly once by `URLComponents.path` in `APIClient`.
|
|
/// Pre-encoding double-encoded MAC colons (`:` → `%3A` → `%253A`), which MCMS
|
|
/// rejected as an invalid id.
|
|
private func enc(_ s: String) -> String { s }
|
|
}
|