import 'dart:convert'; import 'dart:io'; import 'package:http/http.dart'; import 'package:http/io_client.dart'; import 'package:flutter/foundation.dart'; // Combined Riderlog providers: /// Hardcoded known-good IPs for hosts where carrier DNS returns broken nodes. /// /// **Empty since the delivery line moved to Doormile.** The one entry here /// pinned `queue.workolik.com` to 66.116.225.226, because some Indian carriers' /// DNS resolved that host to a CDN node that answered 404. Nothing calls that /// host any more — every delivery-line write goes to `api.doormile.com` via /// [DeliveryApi] — so the pin was routing advice about a server this app no /// longer talks to. /// /// It is deliberately not re-pointed at the Doormile host: that IP belongs to /// the old backend, and pinning it for `api.doormile.com` would send every /// request to the wrong server with the right SNI. If Doormile ever needs the /// same workaround, add it here with its own verified address. const _knownGoodIPs = {}; /// Creates an IOClient that: /// 1. Bypasses SSL certificate errors /// 2. Forces known-good IPs for hosts where carrier DNS returns broken CDN nodes /// 3. Manually does TLS upgrade with correct SNI (hostname, not IP) IOClient _buildSslBypassClient() { final httpClient = HttpClient() ..badCertificateCallback = (X509Certificate cert, String host, int port) => true; httpClient .connectionFactory = (Uri uri, String? proxyHost, int? proxyPort) async { final host = uri.host; final port = uri.port; // Use known-good IP if available, else resolve normally (prefer IPv4) InternetAddress? target; final knownIP = _knownGoodIPs[host]; if (knownIP != null) { target = InternetAddress(knownIP); debugPrint('[SSL_CLIENT] Using known-good IP: $knownIP for $host'); } else { try { final addresses = await InternetAddress.lookup( host, type: InternetAddressType.IPv4, ); if (addresses.isNotEmpty) target = addresses.first; } catch (_) {} } if (uri.scheme == 'https') { final socketFuture = Socket.connect(target ?? InternetAddress(host), port) .then( (plain) => SecureSocket.secure( plain, host: host, // SNI = original hostname for Nginx routing onBadCertificate: (_) => true, supportedProtocols: ['http/1.1'], ), ) .then((s) => s as Socket); return ConnectionTask.fromSocket(socketFuture, () {}); } return Socket.startConnect(target ?? InternetAddress(host), port); }; return IOClient(httpClient); } class CreateRiderLogProvider { Future?> createRiderLog( String urldata, Map data, ) async { const maxAttempts = 3; try { debugPrint('createRiderLog payload ${json.encode(data)}'); } catch (_) {} for (int attempt = 1; attempt <= maxAttempts; attempt++) { final client = _buildSslBypassClient(); try { final url = Uri.parse(urldata); final response = await client.post( url, body: json.encode(data), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); debugPrint('createRiderLog url $urldata (attempt $attempt)'); debugPrint('createRiderLog status ${response.statusCode}'); debugPrint('createRiderLog response ${response.body}'); if (response.statusCode >= 200 && response.statusCode < 300) { return json.decode(response.body.toString()) as Map; } else { debugPrint( 'createRiderLog failed: HTTP ${response.statusCode} (attempt $attempt/$maxAttempts)', ); // On 404/5xx, wait and retry to potentially hit a different CDN node if (attempt < maxAttempts) { await Future.delayed(const Duration(seconds: 1)); } } } catch (e) { debugPrint('createRiderLog exception (attempt $attempt): $e'); if (attempt < maxAttempts) { await Future.delayed(const Duration(seconds: 1)); } } finally { client.close(); } } debugPrint('createRiderLog failed after $maxAttempts attempts'); return null; } } class UpdateRiderLogProvider { Future?> updateRiderLog( String urldata, Map data, ) async { try { final url = Uri.parse(urldata); final response = await put( url, body: json.encode(data), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); debugPrint('updateRiderLog url: $urldata'); debugPrint('updateRiderLog response: ${response.body}'); if (response.statusCode >= 200 && response.statusCode < 300) { final decoded = json.decode(response.body); if (decoded is Map) { return decoded; } else { debugPrint( '⚠️ updateRiderLog: Expected Map but got ${decoded.runtimeType}', ); return {}; } } else { debugPrint('❌ updateRiderLog failed with code ${response.statusCode}'); return {}; } } catch (e) { debugPrint('❌ Exception in updateRiderLog: $e'); return {}; } } } class GetRiderLogProvider { Future?> getRiderLog(String urldata) async { Map? getRiderLogResponse; try { final url = Uri.parse(urldata); final response = await get( url, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); debugPrint('getRiderLog response ${response.body}'); debugPrint('getRiderLog url ${urldata.toString()}'); getRiderLogResponse = json.decode(response.body.toString()) as Map; } catch (e) { debugPrint(e.toString()); } return getRiderLogResponse; } Future?> getRiderCount(String urldata) async { Map? getRiderCountResponse; try { final url = Uri.parse(urldata); final response = await get( url, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); debugPrint('getRiderCount response ${response.body}'); debugPrint('getRiderCount url ${urldata.toString()}'); getRiderCountResponse = json.decode(response.body.toString()) as Map; } catch (e) { debugPrint(e.toString()); } return getRiderCountResponse; } } class BreakRiderLogProvider { Future?> createBreakRiderLog( String urldata, Map data, ) async { Map? breakLogResponse; try { final url = Uri.parse(urldata); final response = await post( url, body: json.encode(data), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); debugPrint('createBreakRiderLog url $urldata'); debugPrint('createBreakRiderLog response ${response.body}'); breakLogResponse = json.decode(response.body.toString()) as Map; } catch (e) { debugPrint(e.toString()); } return breakLogResponse; } Future?> updateBreakRiderLog( String urldata, Map data, ) async { Map? breakLogResponse; try { final url = Uri.parse(urldata); final response = await put( url, body: json.encode(data), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); debugPrint('updateBreakRiderLog url $urldata'); debugPrint('updateBreakRiderLog response ${response.body}'); breakLogResponse = json.decode(response.body.toString()) as Map; } catch (e) { debugPrint(e.toString()); } return breakLogResponse; } }