Harden App Store build for review
This commit is contained in:
@@ -1,28 +1,16 @@
|
||||
// MirrorFuelProvider.swift — GitHub price-mirror provider (P0 live chain).
|
||||
//
|
||||
// The mirror (aptonline/fuelboard-data, pushed daily by the LAN relay) is the
|
||||
// PRODUCTION data source: its day files are the raw relay /api/v1/stations
|
||||
// envelope, so the shared decode works verbatim. Fetching from
|
||||
// raw.githubusercontent.com makes live prices work fully off-LAN — App
|
||||
// Review cannot reach a LAN relay, and consumers never have one to reach —
|
||||
// and the bundled dump (app target) is the no-network last resort.
|
||||
// The mirror (aptonline/fuelboard-data, pushed daily by the ingestion relay)
|
||||
// is the PRODUCTION data source: its day files use the same /api/v1/stations
|
||||
// envelope, so the shared decoder works verbatim. Fetching from
|
||||
// raw.githubusercontent.com makes live prices work fully off-LAN — App Review
|
||||
// cannot reach a private LAN relay, and consumers never have one to reach —
|
||||
// with the bundled dump (app target) as the no-network last resort.
|
||||
//
|
||||
// Chain (LiveChainProvider) — GitHub first, EVERYWHERE:
|
||||
// GitHub mirror → [dev-only LAN relay] → (app target: bundled dump in the
|
||||
// ContentView catch, widget: cache → placeholder)
|
||||
// The relay is NOT part of the consumer chain at all: it is unreachable from
|
||||
// any phone outside the developer's LAN, and even attempting it from a
|
||||
// consumer phone would fire the iOS Local Network permission prompt for an
|
||||
// address that can never be reached. It remains reachable only behind the
|
||||
// hidden dev flag `fuelboard.relayFallback` (Settings → Debug) for home
|
||||
// testing. The relay keeps its server-side job (GOV.UK ingest + daily mirror
|
||||
// push) unchanged.
|
||||
//
|
||||
// Telemetry (FuelBeacon): raw GitHub fetches are invisible to repo analytics
|
||||
// (page views/clones only), so every chain fetch opportunistically pings the
|
||||
// relay's existing widget-diag route (zero relay changes) — but ONLY when the
|
||||
// same dev flag is on, so consumers never make a local-network attempt.
|
||||
// /stats app-hit spikes (dev flag on) = GitHub path failing → fallback active.
|
||||
// Chain (LiveChainProvider) — public HTTPS only:
|
||||
// GitHub mirror → (app target: bundled dump in the ContentView catch,
|
||||
// widget: cache → placeholder)
|
||||
// No local-network fallback or private IP is compiled into the app binary.
|
||||
|
||||
import Foundation
|
||||
|
||||
@@ -126,80 +114,24 @@ struct MirrorFuelProvider: FuelPriceProviding {
|
||||
|
||||
// MARK: - Live chain provider
|
||||
|
||||
/// The live price chain — GitHub first, everywhere. The LAN relay is NOT part
|
||||
/// of the consumer chain: consumers can never reach it (and must never even
|
||||
/// attempt it — that would prompt for Local Network access on a stranger's
|
||||
/// phone). It only joins behind the hidden dev flag `fuelboard.relayFallback`
|
||||
/// (Settings → Debug) for the developer's own home testing. Whichever leg
|
||||
/// serves is recorded for the About section + telemetry.
|
||||
/// The live price chain — public GitHub mirror only. No private LAN fallback is
|
||||
/// compiled into the app binary, so App Store/TestFlight builds cannot trigger
|
||||
/// Local Network permission or leak a private IP.
|
||||
struct LiveChainProvider: FuelPriceProviding {
|
||||
var mirror: FuelPriceProviding
|
||||
var relay: FuelPriceProviding
|
||||
|
||||
/// Which leg served the last fetch — Settings → About metadata + beacon.
|
||||
/// Metadata surfaced in Settings → About.
|
||||
static var latestMeta: RelayMeta?
|
||||
static var lastSource: String?
|
||||
|
||||
init(mirror: FuelPriceProviding = MirrorFuelProvider(),
|
||||
relay: FuelPriceProviding = RelayFuelProvider()) {
|
||||
init(mirror: FuelPriceProviding = MirrorFuelProvider()) {
|
||||
self.mirror = mirror
|
||||
self.relay = relay
|
||||
}
|
||||
|
||||
func fetchStations(near lat: Double?, lng: Double?, fuel: FuelType, radiusKM: Double?) async throws -> [FuelStation] {
|
||||
do {
|
||||
let stations = try await mirror.fetchStations(near: lat, lng: lng, fuel: fuel, radiusKM: radiusKM)
|
||||
Self.latestMeta = MirrorFuelProvider.latestMeta
|
||||
Self.lastSource = "github"
|
||||
FuelBeacon.fire(source: "github", n: stations.count)
|
||||
return stations
|
||||
} catch {
|
||||
// Dev-only fallback: consumers never reach this branch — the flag
|
||||
// is off by default and the flag itself lives in the Debug section.
|
||||
guard FuelStore.loadRelayFallbackEnabled() else { throw error }
|
||||
let stations = try await relay.fetchStations(near: lat, lng: lng, fuel: fuel, radiusKM: radiusKM)
|
||||
Self.latestMeta = RelayFuelProvider.latestMeta
|
||||
Self.lastSource = "relay"
|
||||
FuelBeacon.fire(source: "relay", n: stations.count)
|
||||
return stations
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Telemetry beacon
|
||||
|
||||
/// Fire-and-forget attribution for the LIVE chain (see header note). Never
|
||||
/// awaited, never user-visible; a failed ping is silent. Fires ONLY when the
|
||||
/// dev relay flag is on — consumers must never make a local-network attempt,
|
||||
/// and an off-LAN ping failing silently is itself the reachability datum.
|
||||
/// Disabled in unit tests via `isEnabled`.
|
||||
enum FuelBeacon {
|
||||
static var isEnabled = true
|
||||
|
||||
static var shouldFire: Bool {
|
||||
isEnabled && FuelStore.loadRelayFallbackEnabled()
|
||||
}
|
||||
|
||||
static func fire(source: String, n: Int, timeout: TimeInterval = 2) {
|
||||
guard shouldFire,
|
||||
let url = beaconURL(source: source, n: n) else { return }
|
||||
var request = RelayFuelProvider.relayRequest(url, client: "app", timeout: timeout)
|
||||
request.cachePolicy = .reloadIgnoringLocalCacheData
|
||||
Task { _ = try? await URLSession.shared.data(for: request) }
|
||||
}
|
||||
|
||||
/// Testable URL construction — hits the relay's existing widget-diag
|
||||
/// beacon route with app attribution (zero relay changes).
|
||||
static func beaconURL(source: String, n: Int, base: URL = RelayFuelProvider().baseURL) -> URL? {
|
||||
guard var components = URLComponents(
|
||||
url: base.appendingPathComponent("api/v1/widget-diag"),
|
||||
resolvingAgainstBaseURL: false
|
||||
) else { return nil }
|
||||
components.queryItems = [
|
||||
URLQueryItem(name: "intent", value: "app-live"),
|
||||
URLQueryItem(name: "source", value: source),
|
||||
URLQueryItem(name: "n", value: String(n)),
|
||||
]
|
||||
return components.url
|
||||
let stations = try await mirror.fetchStations(near: lat, lng: lng, fuel: fuel, radiusKM: radiusKM)
|
||||
Self.latestMeta = MirrorFuelProvider.latestMeta
|
||||
Self.lastSource = "github"
|
||||
return stations
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user