Extract duplicated retry logic from OverpassService and RoutingService
into a shared resilience framework in service_policy.dart:
- ResiliencePolicy: configurable retries, backoff, and HTTP timeout
- executeWithFallback: retry loop with primary→fallback endpoint chain
- ErrorDisposition enum: abort / fallback / retry classification
- ServicePolicy + ServicePolicyResolver: per-service compliance rules
(rate limits, caching, concurrency) for OSMF and third-party services
- ServiceRateLimiter: async semaphore-based concurrency and rate control
OverpassService now hits overpass.deflock.org first, falls back to
overpass-api.de. RoutingService hits api.dontgetflocked.com first,
falls back to alprwatch.org. Both use per-service error classifiers
to determine retry vs fallback vs abort behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create UserAgentClient (http.BaseClient wrapper) that injects a
User-Agent header into every request, reading app name and version
from VersionService and contact/homepage from dev_config.dart.
Format follows OSM tile usage policy:
DeFlock/<version> (+https://deflock.org; contact: admin@stopflock.com)
Replaces 4 inconsistent hardcoded UA strings and adds UA to the 9
call sites that previously sent none.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Gate full error response body logging behind kDebugMode; truncate to
500 chars in release builds to avoid log noise and data exposure
- Add RoutingService.close() and call from NavigationState.dispose()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Built-in profiles (Flock, Motorola, etc.) include placeholder empty
values like camera:mount: '' for user refinement. When these get
serialized into the routing request body, the alprwatch API rejects
them with HTTP 400.
Fix: strip empty-valued tags from enabled_profiles before sending
the routing request. Also refactor RoutingService to accept an
injectable http.Client for testability, and log error response
bodies for easier debugging of future API issues.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>