80 lines
3.0 KiB
Dart
80 lines
3.0 KiB
Dart
// turn_credentials_service.dart — جلب بيانات اعتماد TURN المؤقتة وتخزينها
|
|
//
|
|
// خادم الإشارات (Node) يُعيد STUN فقط، وSTUN وحده لا يكفي حين يكون الطرفان
|
|
// خلف CGNAT — وهي الحالة الغالبة لسائق وزبون على بيانات الجوال. نجلب TURN
|
|
// من الباك إند ونضمّه إلى قائمة ICE قبل إنشاء الاتصال.
|
|
//
|
|
// البيانات مؤقتة (HMAC بمهلة) لا كلمة مرور ثابتة داخل التطبيق، ونُخزّنها
|
|
// محلياً حتى قرب انتهائها فلا نُثقل بنداء شبكة قبل كل مكالمة.
|
|
import 'dart:convert';
|
|
|
|
import '../constant/box_name.dart';
|
|
import '../constant/links.dart';
|
|
import '../controller/functions/crud.dart';
|
|
import '../main.dart';
|
|
import '../print.dart';
|
|
|
|
class TurnCredentialsService {
|
|
static const String _cacheKey = BoxName.turnIceCache;
|
|
|
|
/// خوادم ICE جاهزة لتمريرها إلى createPeerConnection.
|
|
/// تعيد قائمة فارغة عند أي فشل — والمُنادي يكمل بـ STUN كما كان سابقاً.
|
|
static Future<List<Map<String, dynamic>>> getIceServers() async {
|
|
final cached = _readCache();
|
|
if (cached != null) return cached;
|
|
|
|
try {
|
|
final res = await CRUD().post(
|
|
link: '${AppLink.server}/ride/call/turn_credentials.php',
|
|
);
|
|
|
|
if (res is! Map || res['status'] != 'success') return [];
|
|
|
|
// غلاف jsonSuccess يضع الحمولة في message
|
|
final payload = res['message'];
|
|
if (payload is! Map || payload['ice_servers'] is! List) return [];
|
|
|
|
final servers = (payload['ice_servers'] as List)
|
|
.whereType<Map>()
|
|
.map((s) => Map<String, dynamic>.from(s))
|
|
.toList();
|
|
|
|
final ttl = int.tryParse(payload['ttl']?.toString() ?? '0') ?? 0;
|
|
if (ttl > 0 && servers.isNotEmpty) {
|
|
// ننتهي قبل الخادم بخمس دقائق تفادياً لسباق انتهاء الصلاحية أثناء مكالمة
|
|
final expiresAt = DateTime.now().add(Duration(seconds: ttl - 300));
|
|
box.write(_cacheKey, jsonEncode({
|
|
'expires_at': expiresAt.toIso8601String(),
|
|
'servers': servers,
|
|
}));
|
|
}
|
|
|
|
return servers;
|
|
} catch (e) {
|
|
Log.print('⚠️ [TURN] تعذّر جلب بيانات الاعتماد: $e');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
static List<Map<String, dynamic>>? _readCache() {
|
|
try {
|
|
final raw = box.read(_cacheKey);
|
|
if (raw == null) return null;
|
|
|
|
final data = jsonDecode(raw.toString());
|
|
final expiresAt = DateTime.tryParse(data['expires_at']?.toString() ?? '');
|
|
if (expiresAt == null || DateTime.now().isAfter(expiresAt)) return null;
|
|
|
|
final servers = (data['servers'] as List)
|
|
.whereType<Map>()
|
|
.map((s) => Map<String, dynamic>.from(s))
|
|
.toList();
|
|
return servers.isEmpty ? null : servers;
|
|
} catch (_) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
static void clearCache() => box.remove(_cacheKey);
|
|
}
|